#include "net/buffer.h"

DEFINE_int32(buf_size, 4096, "buffer size");

namespace net {
Buffer::Buffer()
    : buffer_(FLAGS_buf_size) {
  Revert();
}

ssize_t Buffer::ReadFd(int32 fd) {
  if (fd == -1) return -1;
  if (WriteableSize() == 0) {
    Resize(buffer_.size(), false);
  }

  int32 ret;
  u_char tmp_buf[4096];
  struct iovec vec[2];
  vec[0].iov_base = w_pos_;
  vec[0].iov_len = WriteableSize();
  vec[1].iov_base = tmp_buf;
  vec[1].iov_len = 4096;

  do {
    ret = ::readv(fd, vec, 2);
    if (ret == -1 && errno != EINTR) {
      PLOG(WARNING)<< "read fd error: " << fd;
      return -1;
    }
  } while (ret == -1 && errno == EINTR);

  if (vec[1].iov_len != 0) {
    Append(tmp_buf, vec[1].iov_len);
  } else {
    w_pos_ += ret;
  }

  CHECK_LE(w_pos_, end_);
  return ret;
}

void Buffer::Resize(size_t size, bool reinit) {
  if (size == 0) size = FLAGS_buf_size;
  size_t read_size, write_size;
  if (!reinit) {
    CHECK(size >= buffer_.size());
    read_size = r_pos_ - mem_;
    write_size = w_pos_ - mem_;
  }

  buffer_.resize(size);

  if (!reinit) {
    mem_ = (u_char*) &buffer_[0];
    r_pos_ = mem_ + read_size;
    w_pos_ = mem_ + write_size;
    end_ = mem_ + size;

    return;
  }

  Revert();
}

u_char* Buffer::Retrieve(size_t size) {
  CHECK(ReadableSize() >= size);
  u_char* pos = r_pos_;
  r_pos_ += size;
  return pos;
}

void Buffer::Append(const u_char* buf, size_t size) {
  if (size > WriteableSize()) {
    if (!WriteableSize() + (r_pos_ - mem_)) {
      Resize((end_ - mem_) * 2, false);
    } else {
      for (u_char* pos = mem_; r_pos_ != w_pos_; ++r_pos_) {
        *pos = *r_pos_;
        ++pos;
      }
      w_pos_ -= r_pos_ - mem_;
      r_pos_ = mem_;
    }
  }
  memcpy(w_pos_, buf, size);
  w_pos_ += size;
}

}
